using System;
using System.Collections.Concurrent;
using System.Net;
using System.Net.Sockets;

namespace Hsenl.Network {
    public class UdpIOCPSender : IOCPSender, IUdpSender {
        public event Func<int, (Memory<byte> memory, EndPoint remoteEndPoint)> SendBufferGetter;
        public event Func<int, (byte[] buffer, int offset, int count, EndPoint remoteEndPoint)> SendBufferGetterArray;

        public event Action<EndPoint, Memory<byte>> OnSendToDataEvent;

        public UdpIOCPSender(AddressFamily addressFamily, SocketBufferKind bufferKind) : base(new Socket(addressFamily, SocketType.Dgram, ProtocolType.Udp),
            bufferKind) {
#if NET8_0
            // net8.0有个问题, 就是同一个seae, 不能通过设置不同的 remoteIPEndPoint, 来给不同的socket发消息, 这个问题在9.0被修复了
            // https://github.com/dotnet/runtime/issues/97965
            throw new NotImplementedException("UdpSocket is not supported on net8.0");
#endif
        }

        public UdpIOCPSender(Socket socket, SocketBufferKind bufferKind) : base(socket, bufferKind) {
#if NET8_0
            // net8.0有个问题, 就是同一个seae, 不能通过设置不同的 remoteIPEndPoint, 来给不同的socket发消息, 这个问题在9.0被修复了
            // https://github.com/dotnet/runtime/issues/97965
            throw new NotImplementedException("UdpSocket is not supported on net8.0");
#endif
        }

        protected override Memory<byte> GetSendBuffer(int length) {
            throw new NotImplementedException();
        }

        protected override Memory<byte> GetSendToBuffer(int length, out EndPoint remoteEndPoint) {
            var tuple = this.SendBufferGetter!.Invoke(length);
            remoteEndPoint = tuple.remoteEndPoint;
            return tuple.memory;
        }

        protected override byte[] GetSendBuffer(int length, out int offset, out int count) {
            throw new NotImplementedException();
        }

        protected override byte[] GetSendToBuffer(int length, out int offset, out int count, out EndPoint remoteEndPoint) {
            var tuple = this.SendBufferGetterArray!(length);
            offset = tuple.offset;
            count = tuple.count;
            remoteEndPoint = tuple.remoteEndPoint;
            return tuple.buffer;
        }

        protected override void OnSendTo(EndPoint remoteEndPoint, Memory<byte> data) {
            this.OnSendToDataEvent?.Invoke(remoteEndPoint, data);
        }

        protected override void OnSendTo(EndPoint remoteEndPoint, byte[] data, int offset, int count) {
            this.OnSendToDataEvent?.Invoke(remoteEndPoint, data.AsMemory(offset, count));
        }

        public bool SendToAsync() {
            return base.SendToAsync();
        }

        public override void Close(bool closeSocket = true) {
            if (this.IsClosed)
                return;

            base.Close(closeSocket);

            this.SendBufferGetter = null;
            this.SendBufferGetterArray = null;
            this.OnSendToDataEvent = null;
        }

        public override void Dispose() {
            if (this.IsDisposed)
                return;

            base.Dispose();

            this.SendBufferGetter = null;
            this.SendBufferGetterArray = null;
            this.OnSendToDataEvent = null;
        }
    }
}